/*
 * File Name:               UIDesignEditor.cs
 *
 * Description:             普通类
 * Author:                  lisiyu
 * Create Date:             2016/08/02
 */

using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.UI;
using Text = UnityEngine.UI.Text;

namespace UnityEditor
{
    /// <summary>
    /// UI设计主窗口 
    /// </summary>
    public class UIDesignEditor : EditorWindow
    {
        [MenuItem("Tools/UI设计工具 %U", false, 10)]
        private static void show()
        {
            var window = GetWindow<UIDesignEditor>("UI设计工具");
            window.Show();
        }

        /// <summary>
        /// UI设计专用场景名称 
        /// </summary>
        private const string mSceneName = "UIDesignScene";

        /// <summary>
        /// UI设计专用Demo场景 
        /// </summary>
        private const string mDemoSceneName = "UIDesignDemo";

        /// <summary>
        /// 程序文档模板 
        /// </summary>
        private const string mDocTemplateName = "UIDesignTemplate.md";

        /// <summary>
        /// 根目录 
        /// </summary>
        private string mRootPath
        {
            get
            {
                var ret = AssetDatabase.GetAssetOrScenePath(Resources.Load(mSceneName)).TrimEnd((mSceneName + ".unity").ToCharArray());
                return ret;
            }
        }

        /// <summary>
        /// 配置项 
        /// </summary>
        private UIDesignConfig mUIConfig;

        private void OnGUI()
        {
            // 如果不在专属场景 则进入专属场景
            var flag = ifNotInUIScene();
            ifNotInDemoScene();
            if (flag) return;

            var root = GameObject.Find("[UIRoot]/[GlobalCanvas]").transform;

            // 核心功能 导出程序接口文档
            GUILayout.Box("导出文档前记得测试下格分辨率哟", GUILayout.ExpandWidth(true));
            if (GUILayout.Button("导出程序接口文档"))
            {
                // 拿到所有节点
                // var root = GameObject.Find("UIRoot/GlobalCanvas").transform;
                var transEdit = root.Find("编辑节点").transform;
                var transTemp = root.Find("临时节点").transform;
                var transReference = root.Find("效果图节点").transform;
                var transInterface = root.Find("接口节点").transform;

                // 检查窗口信息
                if (transEdit.childCount == 0)
                {
                    log("找不到窗口界面"); return;
                }
                else if (transEdit.childCount > 1)
                {
                    log("窗口界面的数量过多"); return;
                }

                var component = transEdit.GetChild(0).GetComponent<UIContext>();
                if (component == null)
                {
                    log("窗口上找不到 UIContext 组件"); return;
                }

                var rootPrefab = PrefabUtility.FindPrefabRoot(component.gameObject);
                if (rootPrefab.name != component.gameObject.name)
                {
                    log("窗口节点不是根节点了 请注意预制件关系"); return;
                }

                // 隐藏部分节点
                transEdit.gameObject.SetActive(true);
                transTemp.gameObject.SetActive(false);
                transReference.gameObject.SetActive(false);
                transInterface.gameObject.SetActive(true);

                // 更新接口节点
                root.gameObject.SetActive(false);
                root.gameObject.SetActive(true);
                UIGameviewEditor.setSize("UIDocument", UIManager.UIResolutionWidth, UIManager.UIResolutionHeight);

                // 导出文档
                EditorCoroutineRunner.StartEditorCoroutine(exportUIDocument(component));
            }

            if (mUIConfig == null)
                mUIConfig = UIDesignConfig.load("UIDesignConfig");

            // 适配测试
            GUILayout.Box("适配测试", GUILayout.ExpandWidth(true));
            foreach (var tmp in mUIConfig.Resolutions)
            {
                if (GUILayout.Button(tmp.Name))
                {
                    UIGameviewEditor.setSize(tmp.Name, tmp.Width, tmp.Height);

                    var _canvasScaler = root.GetComponent<CanvasScaler>();
                    // 动态适配比例
                    var _scale = 1f * tmp.Width / tmp.Height;
                    // 参考分辨率 1280*720,1.777
                    if (_scale > 1f * UIManager.UIResolutionWidth / UIManager.UIResolutionHeight)
                    {   // 1280*640,2
                        _canvasScaler.matchWidthOrHeight = 1;
                    }
                    else
                    {   // 1024*768,1.333 960*640,1.5
                        _canvasScaler.matchWidthOrHeight = 0;
                    }
                    root.gameObject.SetActive(false);
                    root.gameObject.SetActive(true);
                }
            }
        }

        /// <summary>
        /// 如果不在专属场景 则进入专属场景 
        /// </summary>
        /// <returns></returns>
        private bool ifNotInUIScene()
        {
            var ret = !EditorApplication.currentScene.Contains(mSceneName);
            if (ret)
            {
                if (GUILayout.Button("进入ui设计场景"))
                {
                    EditorApplication.SaveCurrentSceneIfUserWantsTo();
                    EditorApplication.OpenScene(mRootPath + mSceneName + ".unity");
                }
            }
            return ret;
        }

        /// <summary>
        /// 1. 如果不在Demo场景则可以进入demo场景
        /// 2. 如果在demo场景则可以开启教程
        /// </summary>
        /// <returns></returns>
        private bool ifNotInDemoScene()
        {
            var ret = !EditorApplication.currentScene.Contains(mDemoSceneName);
            if (ret)
            {
                if (GUILayout.Button("进入教程场景"))
                {
                    EditorApplication.SaveCurrentSceneIfUserWantsTo();
                    EditorApplication.OpenScene(mRootPath + mDemoSceneName + ".unity");
                }
            }
            else
            {
                if (GUILayout.Button("开始教程"))
                {
                    if (!EditorApplication.isPlaying)
                    {
                        EditorApplication.isPlaying = true;

                        // UIManager.Singleton.open<DemoMainWindow> (DemoPathEnum.DemoMainWindow);
                    }
                }
            }
            return ret;
        }

        /// <summary>
        /// 打印日志 
        /// </summary>
        /// <param name="content"></param>
        private void log(string content)
        {
            EditorUtility.DisplayDialog("错误日志", content, "确定");
        }

        /// <summary>
        /// 显示程序接口 
        /// </summary>
        /// <param name="uiinterface"></param>
		private System.Collections.IEnumerator exportImages(UIContext context, string picName)
        {
            // 文档路径
            var prefab = PrefabUtility.GetPrefabParent(context.gameObject);
            prefab = PrefabUtility.FindPrefabRoot(prefab as GameObject);
            var prefabPath = AssetDatabase.GetAssetPath(prefab);
            prefabPath = Regex.Replace(prefabPath, "^[a-zA-z\\/]+Resources", "").Replace(prefab.name + ".prefab", "");

            var root = GameObject.Find("UIRoot/GlobalCanvas").transform;
            var transInterface = root.Find("接口节点").transform;

            // 清空节点
            GameObject.DestroyImmediate(transInterface.gameObject);
            transInterface = new GameObject("接口节点").transform;
            transInterface.SetParent(root, false);

            // 位置列表
            var poss = new List<Vector2>(50);
            var x = -525;
            var y = -325;
            for (int i = x; i <= -x; i += 50)
            {
                poss.Add(new Vector2(i, y));
                poss.Add(new Vector2(i, -y));
            }

            x = -575;
            for (int i = y; i <= -y; i += 50)
            {
                poss.Add(new Vector2(x, i));
                poss.Add(new Vector2(-x, i));
            }

            // 生成点
            if (context.pComponents != null)
            {
                for (int i = 0; i < context.pComponents.Length; i++)
                {
                    var tmp = context.pComponents[i];
                    if (tmp == null || tmp.pComponent == null)
                        continue;

                    var trans = tmp.pComponent.transform;
                    // 组件
                    var obj = new GameObject("tmpPoint");
                    obj.transform.SetParent(transInterface, false);
                    var rt = obj.AddComponent<RectTransform>();
                    var img = obj.AddComponent<Image>();
                    rt.sizeDelta = Vector2.one * 40;

                    // 文本
                    var objTxt = new GameObject("tmpTxtId");
                    objTxt.transform.SetParent(rt, false);
                    var txt = objTxt.AddComponent<UnityEngine.UI.Text>();
                    txt.text = "" + i;
                    txt.fontSize = 30;
                    txt.horizontalOverflow = HorizontalWrapMode.Overflow;
                    txt.verticalOverflow = VerticalWrapMode.Overflow;
                    txt.alignment = TextAnchor.MiddleCenter;
                    txt.color = Color.black;

                    // 位置
                    float length = float.MaxValue;
                    Vector2 curPos = Vector2.zero;
                    foreach (var pos in poss)
                    {
                        var distance = Vector2.Distance(transInterface.InverseTransformPoint(trans.position), pos);
                        if (distance < length)
                        {
                            length = distance;
                            curPos = pos;
                        }
                    }
                    poss.Remove(curPos);
                    rt.localPosition = curPos;

                    // 线
                    var objLine = new GameObject("tmpLine");
                    var rtLine = objLine.AddComponent<RectTransform>();
                    objLine.transform.SetParent(rt, false);
                    rtLine.sizeDelta = new Vector2(2, Vector2.Distance(trans.position, rt.position));
                    var imgLine = objLine.AddComponent<Image>();
                    imgLine.color = Color.red;
                    rtLine.position = Vector3.Lerp(trans.position, rt.position, 0.5f);

                    var direction = rt.position - trans.position;
                    if (direction.x > 0) direction = -direction;
                    var angle = Vector3.Angle(Vector2.up, direction);
                    rtLine.eulerAngles = new Vector3(0, 0, angle);
                }
            }

            // 导出图片
            var filePath = Path.Combine(Application.dataPath.Replace(@"Assets", ""), picName);
            if (File.Exists(filePath))
                File.Delete(filePath);
            Application.CaptureScreenshot(picName);
            while (!File.Exists(filePath))
            {
                yield return new WaitForEndOfFrame();
            }
            var file = new FileInfo(filePath);
            Debug.Log(file.FullName);
            var path = mUIConfig.DocumentDirectory + prefabPath + "/Images/";
            if (!Directory.Exists(path))
                Directory.CreateDirectory(path);
            path = Path.Combine(path, file.Name);
            Debug.Log(path);
            if (File.Exists(path))
                File.Delete(path);
            file.MoveTo(path);

            // 子节点
            if (context.pComponents != null)
            {
                for (int i = 0; i < context.pComponents.Length; i++)
                {
                    var tmp = context.pComponents[i];
                    if (tmp == null || tmp.pComponent == null)
                        continue;

                    var child = tmp.pComponent.GetComponent<UIContext>();
                    if (child != null)
                    {
                        // 导出子节点图片
                        yield return exportImages(child, picName.Replace(".png", "_") + tmp.pComponent.name + ".png");
                    }
                }
            }
        }

        /// <summary>
        /// 导出文档 
        /// </summary>
        private System.Collections.IEnumerator exportUIDocument(UIContext component)
        {
            // 显示进度条 EditorUtility.DisplayProgressBar("");

            // 设置缩放
            var root = GameObject.Find("UIRoot/GlobalCanvas").transform;
            var transEdit = root.Find("编辑节点").transform;

            transEdit.localScale = Vector3.one * 0.8f;

            // 导出图片
            yield return exportImages(component, component.name + ".png");

            // 编写文档
            var template = File.ReadAllText(Path.Combine(mRootPath, mDocTemplateName));
            Debug.Log(template);

            // 文档路径
            var prefab = PrefabUtility.GetPrefabParent(component.gameObject);
            prefab = PrefabUtility.FindPrefabRoot(prefab as GameObject);
            var prefabPath = AssetDatabase.GetAssetPath(prefab);
            prefabPath = Regex.Replace(prefabPath, "^[a-zA-z\\/]+Resources", "").Replace(prefab.name + ".prefab", "");

            var docPath = mUIConfig.DocumentDirectory + prefabPath;
            docPath = Path.Combine(docPath, prefab.name + ".md");
            if (File.Exists(docPath))
            {
                File.Delete(docPath);
            }

            using (var fs = File.CreateText(docPath))
            {
                // 填充文档
                template = praseWindow(template, component);

                // 写入模板内容
                fs.Write(template);
            }

            transEdit.localScale = Vector3.one;

            var transInterface = root.Find("接口节点").transform;
            transInterface.gameObject.SetActive(false);
        }

        private string praseWindow(string template, UIContext window)
        {
            var ret = template;

            ret = ret.Replace("$w_name$", window.name);

            ret = parseLayer(ret, window.GetComponent<UILayer>());
            ret = parseGroup(ret, window.GetComponent<UIGroup>());
            ret = parseLanguage(ret, window.GetComponent<UILanguage>());
            ret = parseParam(ret, window.GetComponent<UIParam>());

            // 写入 Context 列表
            ret = Regex.Replace(ret, "<context>(.*)</context>", (match) =>
            {
                var temp = match.Groups[1].Value;
                return praseDocInterfaceList(window, temp, window.name);
            }, RegexOptions.Singleline);

            return ret;
        }

        private string parseLayer(string template, UILayer layer)
        {
            var ret = template;

            ret = hideToggle(ret, "{layer}(.*){/layer}", layer);

            if (layer == null)
                return ret;
            ret = ret.Replace("$w_layer$", layer.pLayer.ToString());

            return ret;
        }

        private string parseGroup(string template, UIGroup group)
        {
            var ret = template;

            ret = hideToggle(ret, "{group}(.*){/group}", group);

            if (group == null)
                return ret;

            ret = ret.Replace("$g_id$", group.pGroup.ToString());
            ret = ret.Replace("$g_mutex$", intArray2Str(group.pMutexGroups));
            ret = ret.Replace("$g_depend$", intArray2Str(group.pDependGroups));

            return ret;
        }

        private string parseLanguage(string template, UILanguage language)
        {
            var ret = template;

            ret = hideToggle(ret, "{language}(.*){/language}", language);

            if (language == null)
                return ret;

            ret = praseDoc(ret, "<language>(.*)</language>", language.pLanguages, parseLanguage);

            return ret;
        }

        private string parseLanguage(int id, UILanguage.Data data, string template)
        {
            var ret = template;

            ret = ret.Replace("$l_id$", id + "");
            ret = ret.Replace("$l_content$", data.pLanguageId + "");
            ret = ret.Replace("$l_description$", data.pDescription);

            return ret;
        }

        private string parseParam(string template, UIParam param)
        {
            var ret = template;

            ret = hideToggle(ret, "{param}(.*){/param}", param);

            if (param == null)
                return ret;

            ret = praseDoc(ret, "<param>(.*)</param>", param.pParams, parseParam);

            return ret;
        }

        private string parseParam(int id, UIParam.Data data, string template)
        {
            var ret = template;

            ret = ret.Replace("$p_id$", id + "");
            ret = ret.Replace("$p_content$", data.pContent);
            ret = ret.Replace("$p_description$", data.pDescription);

            return ret;
        }

        private string praseDocInterfaceList(UIContext component, string temp, string picname)
        {
            var childInterface = praseDocInterface(component, temp, picname);
            foreach (var child in component.pComponents)
            {
                if (child == null || child.pComponent == null) continue;
                var context = child.pComponent.GetComponent<UIContext>();
                if (context == null) continue;
                childInterface += praseDocInterfaceList(context, temp, picname + "_" + child.pComponent.name);
            }
            return childInterface;
        }

        private string praseDocInterface(UIContext component, string content, string picname)
        {
            var ret = content;
            ret = ret.Replace("$i_name$", component.name);
            ret = ret.Replace("$i_picname$", "Images/" + picname);

            ret = praseDoc(ret, "<component>(.*)</component>", component.pComponents, praseDocComponent);

            return ret;
        }

        private string hideToggle(string template, string pattern, bool flag)
        {
            return Regex.Replace(template, pattern, (match) =>
                {
                    if (flag)
                    {
                        return match.Groups[1].Value;
                    }
                    else
                    {
                        return "";
                    }
                }, RegexOptions.Singleline);
        }

        private delegate string PraseDoc<T>(int id, T component, string template);

        private string praseDoc<T>(string content, string pattern, T[] components, PraseDoc<T> callback)
        {
            return Regex.Replace(content, pattern, (match) =>
            {
                var temp = match.Groups[1].Value;
                var desc = "";
                if (components == null) return desc;
                for (int id = 0; id < components.Length; id++)
                {
                    desc += callback(id, components[id], temp);
                }
                return desc;
            }, RegexOptions.Singleline);
        }

        private string praseDocComponent(int id, UIContext.Data child, string template)
        {
            if (child.pComponent == null)
            {
                log("有空的组件引用 请检查并排除！");
                return "";
            }

            var context = child.pComponent.GetComponent<UIContext>();

            var ret = template;
            ret = ret.Replace("$c_id$", id + "");
            ret = ret.Replace("$c_child$", context == null ? "false" : "true");
            var components = child.pComponent.GetComponents<Component>();
            string str = "";
            for (int i = 0; i < components.Length; i++)
            {
                var tmp = components[i];
                if (tmp is CanvasRenderer) continue;
                if (tmp is Transform) continue;

                str += tmp.GetType().Name;
                if (i < components.Length - 1) str += " + ";
            }
            ret = ret.Replace("$c_components$", str);
            ret = ret.Replace("$c_description$", child.pDescription);

            return ret;
        }

        private string intArray2Str(int[] args)
        {
            if (args == null)
                return "";

            var ret = "";
            for (int i = 0; i < args.Length; i++)
            {
                if (i > 0)
                    ret += "+";
                ret += args[i];
            }
            return ret;
        }
    }
}